home *** CD-ROM | disk | FTP | other *** search
/ Giga Games 1 / Giga Games.iso / net / usenet / volume6 / puzzle15.2 < prev    next >
Encoding:
Internet Message Format  |  1989-03-15  |  33.0 KB

  1. Path: uunet!tektronix!tekgen!tekred!games
  2. From: games@tekred.CNA.TEK.COM
  3. Newsgroups: comp.sources.games
  4. Subject: v06i026:  puzzle15-2 - revised puzzle15 (15 square puzzle)
  5. Message-ID: <3721@tekred.CNA.TEK.COM>
  6. Date: 15 Mar 89 22:36:07 GMT
  7. Sender: billr@tekred.CNA.TEK.COM
  8. Lines: 1163
  9. Approved: billr@saab.CNA.TEK.COM
  10.  
  11. Submitted-by: "P. Knoppers" <nluug.nl!duteca!knop@tektronix>
  12. Posting-number: Volume 6, Issue 26
  13. Archive-name: puzzle15-2
  14.  
  15. #! /bin/sh
  16. # This is a shell archive.  Remove anything before this line, then unpack
  17. # it by saving it into a file and typing "sh file".  To overwrite existing
  18. # files, type "sh file -c".  You can also feed this as standard input via
  19. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  20. # will see the following message at the end:
  21. #        "End of shell archive."
  22. # Contents:  README Makefile puzzle15.6 puzzle15.c
  23. # Wrapped by billr@saab on Wed Mar 15 14:36:49 1989
  24. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  25. if test -f 'README' -a "${1}" != "-c" ; then 
  26.   echo shar: Will not clobber existing file \"'README'\"
  27. else
  28. echo shar: Extracting \"'README'\" \(604 characters\)
  29. sed "s/^X//" >'README' <<'END_OF_FILE'
  30. XI have applied some of the suggestions put to me by various persons
  31. X(acknowledged in the program text). The program can be compiled with
  32. Xthe option -DARROWKEYS, but I am not sure that it works - I cannot
  33. Xtest it. The other compile-time options have been tested and they work.
  34. X
  35. XIn the following shell archive you will find the source of the program
  36. Xand a manual page in [nt]roff -man format.
  37. X
  38. XSince the previous version I have moved to another machine. My email
  39. Xaddress is now knop@duteca.UUCP. Mail to the previous machine may not
  40. Xbounce, in which case it might appear in my mailbox on the new machine.
  41. X
  42. END_OF_FILE
  43. if test 604 -ne `wc -c <'README'`; then
  44.     echo shar: \"'README'\" unpacked with wrong size!
  45. fi
  46. # end of 'README'
  47. fi
  48. if test -f 'Makefile' -a "${1}" != "-c" ; then 
  49.   echo shar: Will not clobber existing file \"'Makefile'\"
  50. else
  51. echo shar: Extracting \"'Makefile'\" \(101 characters\)
  52. sed "s/^X//" >'Makefile' <<'END_OF_FILE'
  53. X# simple makefile for puzzle15
  54. Xpuzzle15: puzzle15.c
  55. X    cc -o puzzle15 -O puzzle15.c -lcurses -ltermcap
  56. END_OF_FILE
  57. if test 101 -ne `wc -c <'Makefile'`; then
  58.     echo shar: \"'Makefile'\" unpacked with wrong size!
  59. fi
  60. # end of 'Makefile'
  61. fi
  62. if test -f 'puzzle15.6' -a "${1}" != "-c" ; then 
  63.   echo shar: Will not clobber existing file \"'puzzle15.6'\"
  64. else
  65. echo shar: Extracting \"'puzzle15.6'\" \(6379 characters\)
  66. sed "s/^X//" >'puzzle15.6' <<'END_OF_FILE'
  67. X.TH PUZZLE15 6
  68. X.UC 4
  69. X.SH NAME
  70. Xpuzzle15 \- simulate a well known toy with many variations
  71. X.SH SYNOPSIS
  72. X.br
  73. X.B puzzle15
  74. X[<width[x<height>]]
  75. X[movemethod]
  76. X[cursormethod]
  77. X[facemethod]
  78. X.SH DESCRIPTION
  79. X.I Puzzle15
  80. Xsimulates a game that is available in many toy shops. This computerized
  81. Xversion is crt-oriented and offers fewer possibilities for cheating. 
  82. X.br
  83. XWhen invoked without arguments
  84. X.I puzzle15
  85. Xpresents a 4 by 4 grid with 15 numbered tiles and one empty square.
  86. XThe tiles are not ordered in sequence.
  87. XThe tiles adjacent to the empty square can exchange places with the
  88. Xempty square. This is done by indicating the direction that the empty 
  89. Xsquare is to move with the vi(1) cursor keys (k for up, j for down, 
  90. Xh for left and l for right). 
  91. X.br
  92. XThe intention of the game is to rearrange the tiles in their ``natural''
  93. Xsequence. 
  94. X.PP
  95. XThe program keeps track of the time and the number of moves used.
  96. XThis makes it possible to arrange competitions where time, or the
  97. Xnumber of moves used determines the winner.
  98. X.PP
  99. XThe program ends when the tiles are arranged in sequence, or when you
  100. Xtype your interrupt character (usually ctrl-C or break). On computers
  101. Xthat lack good support for keyboard interrupts a key can be selected 
  102. Xthat will terminate the program. (This is a compile-time option.)
  103. X.SH VARIATIONS
  104. XThe look and feel of the program can be altered by specifying different
  105. Xdimensions for the array, the way that the tile to move is selected
  106. X(movemethod), the keys that specify where the cursor is to go 
  107. X(cursormethod) and the faces that are shown on the tiles (facemethod).
  108. X.SH DIMENSIONS
  109. XThe dimensions of the board can be varied within the limits of your
  110. Xterminal screen. To obtain a square array a single numeric argument
  111. Xis required. 
  112. X.I Puzzle15 
  113. X.I 3
  114. Xpresents a 3x3 array with 8 numbered tiles. With numbered tiles it
  115. Xis not possible to extend the array beyond 10 by 10, because only two
  116. Xdigits are used to identify each tile.
  117. X.PP
  118. XTo obtain a non-square array an argument that consists of a number, 
  119. Xthe character 'x' and another number must be supplied.
  120. X.I Puzzle15
  121. X.I 6x3
  122. Xpresents a grid that is 6 columns wide and 3 rows high with 17
  123. Xnumbered tiles.
  124. X.SH MOVEMETHODS
  125. XThe default movemethod is to indicate the direction that the empty
  126. X(blank) field is to move. This is called
  127. X.I moveblank.
  128. XThere are two other methods that can be selected by an argument on the
  129. Xcommand line.
  130. X.br
  131. X.I Movetile
  132. Xtells 
  133. X.I puzzle15
  134. Xthat you want to indicate the direction that an adjacent tile is to be 
  135. Xmoved. This is equivalent to reversal of the directions of the cursor 
  136. Xkeys.
  137. X.br
  138. X.I Selecttile
  139. Xtells
  140. X.I puzzle15
  141. Xthat you want to indicate the new position of the empty field by
  142. Xmoving the cursor to that position and then typing a <space>, or
  143. X<return> (or whatever you like). In this way you can move several
  144. Xtiles in one blow.
  145. X.SH CURSORMETHODS
  146. XThe default way to move the cursor or the empty field is to use the
  147. Xvi(1) cursor keys.
  148. X.br
  149. XOn some computers it is possible to specify
  150. X.I arrowkeys,
  151. Xwhich tells 
  152. X.I puzzle15
  153. Xto use the arrow keys of your terminal to specify a move. 
  154. X.br
  155. XIf you specify 
  156. X.I numberkeys
  157. Xon the command line, 
  158. X.I puzzle15 
  159. Xwill use '8' for up, '2' for down, '4' for left and '6' for right.
  160. Xthis corresponds to arrows on the numeric key pad of most personal 
  161. Xcomputers.
  162. X.br
  163. XAnother way to specify where the empty field is to go is obtained by
  164. Xspecifying
  165. X.I facekeys
  166. Xon the command line. In this mode you move the empty field to a new
  167. Xposition by typing the character(s) shown on the tile that currently
  168. Xoccupies that position. 
  169. X.I Facekeys
  170. Ximplies use of the movemethod 
  171. X.I selecttile.
  172. XIf the
  173. X.I facekeys
  174. Xcursormethod is used with numbered tiles, some tile numbers are shown
  175. Xwith leading zero. To select such a tile a leading zero is required.
  176. XTiles that are labeled with a single digit can be selected with that
  177. Xdigit, or with a leading zero followed by that digit.
  178. X.br
  179. XFinally you can specify your own cursor movement keys with
  180. X.I keys
  181. X.I <up><down><left><right>
  182. Xon the command line. <Up> is the key that you will use to move
  183. Xup, etc.
  184. X.SH FACEMETHODS
  185. XThe default way that
  186. X.I puzzle15
  187. Xuses to identify the tiles is to number them. If you prefer
  188. Xtiles that are marked with letters you must specify
  189. X.I alphafaces
  190. Xon the command line. 
  191. X.I Puzzle15
  192. Xwill now use the lower-case letters a..z to identify the tiles.
  193. X.br
  194. XSimilarly, if you specify
  195. X.I numberalphafaces
  196. X.I puzzle15
  197. Xwill use the digits 0..9 followed by the lower-case letters a..z to
  198. Xidentify the tiles.
  199. X.br
  200. X.I Alphanumberfaces
  201. Xselects the lower-case letters a..z followed by the digits 0..9.
  202. X.br
  203. XFinally, you can specify the characters to use on the command line with
  204. X.I faces
  205. X.I <facecharacters>.
  206. XThe order in
  207. X.I <facecharacters>
  208. Xwill be used to determine when the tiles are ordered.
  209. X.SH EXAMPLES
  210. X.I puzzle15 
  211. X.I faces 
  212. X.I '1234567890qwertyuiopasdfghjkl;zxcvbnm,.'
  213. X.I 10x4 
  214. X.br
  215. Xcreates a 10 by 4 array filled with the symbols of a typewriter 
  216. Xkeyboard. The position to achieve is the ordering of the keys on
  217. Xthe typewriter. Quoting is necessary to prevent the shell from
  218. Xtreating the ``;'' as a special character. By adding the option
  219. X.I facekeys
  220. Xthe program turns into a tough typing tutor, especially if you decide
  221. Xnot to take your eyes off the screen.
  222. X.SH DIAGNOSTICS
  223. XAn unknown keyword or an illegal combination of keywords on the command
  224. Xline generates a complete list of possible keywords and restrictions on
  225. Xtheir combinations.
  226. X.br
  227. XIf you type an undefined key during the game, the program lists the keys
  228. Xthat are valid.
  229. X.br
  230. XOn startup fatal diagnostics from the curses screen handling package
  231. Xare possible if your terminal is ill-defined.
  232. X.br
  233. XIf the program detects an internal error a diagnostic starting with the
  234. Xletters
  235. X.I aargh:
  236. Xis printed. If you manage to get one of these, I like to know how
  237. Xyou did it.
  238. X.SH AUTHOR
  239. XP. Knoppers - knop@duteca.UUCP.
  240. X.br
  241. XPaul Lew added arrow keys.
  242. X.br
  243. XBo Kullmar added the clock and the move counter.
  244. X.br
  245. XLarry Hastings suggested the selecttile method.
  246. X.SH COPYRIGHT
  247. XThe
  248. X.I puzzle15
  249. Xprogram is protected by copyright (C) 1984, 1988, 1989 by P. Knoppers. 
  250. XDistribution of unmodified copies of the program with source is unrestricted. 
  251. XYou are not allowed to distribute modified copies. This is to prevent 
  252. Xuncontrolled spreading of a zillion different versions. 
  253. XYou can mail suggestions for improvements to me. I 
  254. X.I may
  255. Xincorporate them in a future version.
  256. END_OF_FILE
  257. if test 6379 -ne `wc -c <'puzzle15.6'`; then
  258.     echo shar: \"'puzzle15.6'\" unpacked with wrong size!
  259. fi
  260. # end of 'puzzle15.6'
  261. fi
  262. if test -f 'puzzle15.c' -a "${1}" != "-c" ; then 
  263.   echo shar: Will not clobber existing file \"'puzzle15.c'\"
  264. else
  265. echo shar: Extracting \"'puzzle15.c'\" \(23213 characters\)
  266. sed "s/^X//" >'puzzle15.c' <<'END_OF_FILE'
  267. X/*
  268. X * This program simulates a simple toy with many many variations.
  269. X *
  270. X * Compile with -DARROWKEYS if your curses library understands about
  271. X * arrow keys (and your keyboards have them).
  272. X * Compile with -DNOSIGNAL if your machine does not support the signal
  273. X * function to catch ctrl-C.
  274. X * Compile with "-DDIECHAR='<somechar>'" if you would like the program
  275. X * to exit gracefully when <somechar> is typed. All the quotes in and
  276. X * around this argument are necessary. The double quotes prevent the
  277. X * shell from stripping the single quotes. Instead of '<somechar' you
  278. X * can also type the ascii value of the key. If you do so, the double
  279. X * quotes are not needed.
  280. X *
  281. X * The copyright message is there to prevent uncontrolled spreading of
  282. X * a zillion different versions.
  283. X *
  284. X * You can mail your improvements and suggestions to me. I may include
  285. X * them in a future version of the program.
  286. X *
  287. X * I do not guarantee the fitness of this program for any purpose.
  288. X * USE IT AT YOUR OWN RISK.
  289. X *
  290. X * Peter Knoppers - knop@duteca.UUCP
  291. X * Bilderdijkhof 59
  292. X * 2624 ZG  Delft
  293. X * The Netherlands
  294. X *
  295. X *
  296. X * The following lines are put in the compiled program by the C compiler
  297. X * where they can be found by programs such as (Bsd) strings.
  298. X */
  299. Xchar   *this_is = "puzzle15 V2.0 Feb 24 1989";
  300. Xchar   *author0 = "(C) Copyright 1985, 1988, 1989 Peter Knoppers";
  301. Xchar   *author1 = "Arrow keys added by Paul Lew 1988";
  302. Xchar   *author2 = "Clock added by Bo Kullmar 1988";
  303. Xchar   *author3 = "Select-tile method suggested by Larry Hastings 1988";
  304. Xchar   *authorx = "Additions merged by Peter Knoppers";
  305. Xchar   *release1 = "Permission to use and redistribute unmodified ";
  306. Xchar   *release2 = "copies with source is granted to everyone";
  307. Xchar   *release3 = "Distribution of modified copies is NOT allowed";
  308. X
  309. X#include <curses.h>
  310. X#ifndef NOSIGNAL
  311. X#include <signal.h>
  312. X#endif
  313. X#include <ctype.h>
  314. X
  315. X#ifndef DIECHAR
  316. X#define DIECHAR 999        /* any non-ASCII value will do */
  317. X#endif
  318. X#define DEFSIZE          4    /* default size of board */
  319. X#define BLANK          0
  320. X#define UNSPECIFIED      (-1)
  321. X/* classification of arguments */
  322. X#define MOVEMETHOD      1
  323. X#define CURSORMETHOD      2
  324. X#define FACEMETHOD      3
  325. X/* magic values are required to be different, no other restrictions apply */
  326. X/* magic values for Movemethods */
  327. X#define MOVEBLANK      4
  328. X#define MOVETILE      5
  329. X#define SELECTTILE      6
  330. X/* magic values for Cursormethods */
  331. X#define VIKEYS          7
  332. X#ifdef ARROWKEYS
  333. X#define ARROWK          8
  334. X#endif
  335. X#define NUMBERKEYS      9
  336. X#define FACEKEYS     10
  337. X#define KEYS         11
  338. X/* magic values for Facemethods */
  339. X#define NUMBERFACES     12
  340. X#define ALPHAFACES     13
  341. X#define NUMBERALPHAFACES 14
  342. X#define ALPHANUMBERFACES 15
  343. X#define FACES         16
  344. X
  345. Xunsigned long   Time_start;
  346. Xint     Movecnt = 0;
  347. X
  348. Xchar   *malloc ();        /* to satisfy lint */
  349. Xlong    time ();
  350. X
  351. Xint     Sizex = UNSPECIFIED;    /* x-size of the board */
  352. Xint     Sizey = UNSPECIFIED;    /* y-size of the board */
  353. Xint     Size2;            /* total surface of the board */
  354. Xint     Movemethod = UNSPECIFIED;
  355. Xint     Cursormethod = UNSPECIFIED;
  356. Xint     Facemethod = UNSPECIFIED;
  357. Xchar   *Movename;        /* name of the selected Movemethod */
  358. Xchar   *Cursorname;        /* name of the selected Cursormethod */
  359. Xchar   *Cursorkeys;        /* the user-defined Cursorkeys */
  360. Xchar   *Facename;        /* name of the selected Facemethod */
  361. Xchar   *Facechars;        /* the user-defined tile-faces */
  362. Xint     Up;            /* key-codes for cursor movements */
  363. Xint     Down;
  364. Xint     Left;
  365. Xint     Right;
  366. Xint     Newpos;            /* new position for empty field */
  367. Xchar   *Myname;            /* name used to invoke this program */
  368. X/*
  369. X * All possible keyword arguments are listed in the Argopts table.
  370. X * This is done in order to
  371. X * - ensure that every keyword appears at only one place in the program
  372. X *   (this makes the program easier to modify/maintain)
  373. X * - put most of the knowledge about incompatible arguments in one place
  374. X *   instead of scattering it all over the program.
  375. X * - simplify the argument parser
  376. X *   (I haven't been completely succesfull in this respect...)
  377. X */
  378. Xstruct argtablemember
  379. X{
  380. X    char   *a_name;        /* what the user types */
  381. X    int     a_type;        /* what kind of spec. it is */
  382. X    int     a_magic;        /* magic value */
  383. X} Argopts[] =
  384. X{
  385. X    {
  386. X        "moveblank", MOVEMETHOD, MOVEBLANK
  387. X    },
  388. X    {
  389. X    "movetile", MOVEMETHOD, MOVETILE
  390. X    },
  391. X    {                /* SELECTTILE must be before FACEKEYS */
  392. X    "selecttile", MOVEMETHOD, SELECTTILE
  393. X    },
  394. X    {
  395. X    "vikeys", CURSORMETHOD, VIKEYS
  396. X    },
  397. X#ifdef ARROWKEYS
  398. X    {
  399. X    "arrowkeys", CURSORMETHOD, ARROWK
  400. X    },
  401. X#endif
  402. X    {
  403. X    "numberkeys", CURSORMETHOD, NUMBERKEYS
  404. X    },
  405. X    {
  406. X    "facekeys", CURSORMETHOD, FACEKEYS
  407. X    },
  408. X    {
  409. X    "keys", CURSORMETHOD, KEYS
  410. X    },
  411. X    {
  412. X    "numberfaces", FACEMETHOD, NUMBERFACES
  413. X    },
  414. X    {
  415. X    "alphafaces", FACEMETHOD, ALPHAFACES
  416. X    },
  417. X    {
  418. X    "numberalphafaces", FACEMETHOD, NUMBERALPHAFACES
  419. X    },
  420. X    {
  421. X    "alphanumberfaces", FACEMETHOD, ALPHANUMBERFACES
  422. X    },
  423. X    {
  424. X    "faces", FACEMETHOD, FACES
  425. X    },
  426. X    {
  427. X    (char *) 0, 0, 0
  428. X    }
  429. X};
  430. X
  431. Xdie ()                /* nice exit on ctrl-C */
  432. X{
  433. X#ifndef NOSIGNAL
  434. X    signal (SIGINT, SIG_IGN);    /* ignore ctrl-C */
  435. X#endif
  436. X    mvprintw (LINES - 1, 0, "Goodbye. ");
  437. X    clrtoeol ();
  438. X    refresh ();
  439. X    endwin ();            /* shutdown curses, restore ttymode */
  440. X    exit (0);
  441. X}
  442. X
  443. Xmain (argc, argv)        /* scan the arguments, call game() */
  444. Xchar  **argv;
  445. X{
  446. X    struct argtablemember  *atmp;/* to walk Argopts table */
  447. X    int     swap;        /* to revers directions */
  448. X
  449. X    Myname = *argv;        /* save this for usage () */
  450. X /* 
  451. X  * scan the arguments
  452. X  */
  453. X    while (*++argv != ((char *) 0))
  454. X    {                /* walk argument list */
  455. X    for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
  456. X    {            /* scan the keyword list */
  457. X        if (strcmp (*argv, atmp -> a_name) == 0)
  458. X        {            /* found keyword */
  459. X        switch (atmp -> a_type)
  460. X        {
  461. X            case MOVEMETHOD: 
  462. X            if (Movemethod != UNSPECIFIED)
  463. X                conflict (Movename, atmp -> a_name);
  464. X            Movename = atmp -> a_name;
  465. X            Movemethod = atmp -> a_magic;
  466. X            break;
  467. X            case CURSORMETHOD: 
  468. X            if (Cursormethod != UNSPECIFIED)
  469. X                conflict (Cursorname, atmp -> a_name);
  470. X            Cursorname = atmp -> a_name;
  471. X            Cursormethod = atmp -> a_magic;
  472. X            if (atmp -> a_magic == KEYS)
  473. X            {
  474. X                if ((Cursorkeys = *++argv) == (char *) 0)
  475. X                novalue (*--argv);/* never returns */
  476. X                if (strlen (Cursorkeys) != 4)
  477. X                {
  478. X                printf ("%s need 4 cursorkeys\n",
  479. X                    Cursorname);
  480. X                usage ();
  481. X                }
  482. X            }
  483. X            break;
  484. X            case FACEMETHOD: 
  485. X            if (Facemethod != UNSPECIFIED)
  486. X                conflict (Facename, atmp -> a_name);
  487. X            Facename = atmp -> a_name;
  488. X            Facemethod = atmp -> a_magic;
  489. X            if (atmp -> a_magic == FACES)
  490. X                if ((Facechars = *++argv) == (char *) 0)
  491. X                novalue (*--argv);
  492. X            break;
  493. X            default: 
  494. X            printf ("aargh: bad switch (atmp -> a_type = %d)\n",
  495. X                atmp -> a_type);
  496. X            exit (1);
  497. X        }
  498. X        break;
  499. X        }
  500. X    }
  501. X    if (atmp -> a_name == (char *) 0)/* not a keyword */
  502. X        if (isdigit (**argv) && (Sizex == UNSPECIFIED))
  503. X        {            /* it's a boardsize specification */
  504. X        if (sscanf (*argv, "%dx%d", &Sizex, &Sizey) != 2)
  505. X            if (sscanf (*argv, "%d", &Sizex) == 1)
  506. X            Sizey = Sizex;
  507. X            else
  508. X            {
  509. X            printf ("bad argument %s\n", *argv);
  510. X            usage ();
  511. X            }
  512. X        }
  513. X        else        /* it's garbage */
  514. X        {
  515. X        printf ("bad argument %s\n", *argv);
  516. X        usage ();
  517. X        }
  518. X    }
  519. X
  520. X /* 
  521. X  * insert default values
  522. X  */
  523. X    if (Sizex == UNSPECIFIED)
  524. X    Sizex = Sizey = DEFSIZE;
  525. X
  526. X    if (Cursormethod == UNSPECIFIED)
  527. X    {                /* find first CURSORMETHOD in Argopts */
  528. X    for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
  529. X        if (atmp -> a_type == CURSORMETHOD)
  530. X        break;
  531. X    if (atmp -> a_name == (char *) 0)
  532. X    {
  533. X        printf ("aargh: can't find default Cursormethod\n");
  534. X        exit (1);
  535. X    }
  536. X    Cursormethod = atmp -> a_magic;
  537. X    Cursorname = atmp -> a_name;
  538. X    }
  539. X
  540. X    if (Facemethod == UNSPECIFIED)
  541. X    {                /* find first FACEMETHOD in Argopts */
  542. X    for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
  543. X        if (atmp -> a_type == FACEMETHOD)
  544. X        break;
  545. X    if (atmp -> a_name == (char *) 0)
  546. X    {
  547. X        printf ("aargh: can't find default Facemethod\n");
  548. X        exit (1);
  549. X    }
  550. X    Facemethod = atmp -> a_magic;
  551. X    Facename = atmp -> a_name;
  552. X    }
  553. X
  554. X    if (Movemethod == UNSPECIFIED)
  555. X    if (Cursormethod == FACEKEYS)
  556. X        Movemethod = SELECTTILE;/* by implication */
  557. X    else
  558. X    {            /* find first MOVEMETHOD in Argopts */
  559. X        for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
  560. X        if (atmp -> a_type == MOVEMETHOD)
  561. X            break;
  562. X        if (atmp -> a_name == (char *) 0)
  563. X        {
  564. X        printf ("aargh: can't find default Movemethod\n");
  565. X        exit (1);
  566. X        }
  567. X        Movemethod = atmp -> a_magic;
  568. X        Movename = atmp -> a_name;
  569. X    }
  570. X
  571. X    if ((Cursormethod == FACEKEYS) && (Movemethod != SELECTTILE))
  572. X    conflict (Cursorname, Movename);/* does not return */
  573. X    switch (Cursormethod)
  574. X    {
  575. X    case VIKEYS: 
  576. X        Up = 'k';
  577. X        Down = 'j';
  578. X        Left = 'h';
  579. X        Right = 'l';
  580. X        break;
  581. X#ifdef ARROWKEYS
  582. X    case ARROWK: 
  583. X        Up = KEY_UP;
  584. X        Down = KEY_DOWN;
  585. X        Left = KEY_LEFT;
  586. X        Right = KEY_RIGHT;
  587. X        break;
  588. X#endif
  589. X    case NUMBERKEYS: 
  590. X        Up = '8';
  591. X        Down = '2';
  592. X        Left = '4';
  593. X        Right = '6';
  594. X        break;
  595. X    case FACEKEYS: 
  596. X        Up = 1000;        /* values must not correspond to any ASCII */
  597. X        Down = 1001;    /*   value, otherwise no restrictions */
  598. X        Left = 1002;
  599. X        Right = 1003;
  600. X        break;
  601. X    case KEYS: 
  602. X        Up = Cursorkeys[0];
  603. X        Down = Cursorkeys[1];
  604. X        Left = Cursorkeys[2];
  605. X        Right = Cursorkeys[3];
  606. X        break;
  607. X    default: 
  608. X        printf ("aargh: bad switch (Cursormethod = %d)\n",
  609. X            Cursormethod);
  610. X        exit (1);
  611. X    }
  612. X
  613. X    if (Movemethod == MOVETILE)
  614. X    {                /* revers cursor directions */
  615. X    swap = Up;
  616. X    Up = Down;
  617. X    Down = swap;
  618. X    swap = Left;
  619. X    Left = Right;
  620. X    Right = swap;
  621. X    }
  622. X
  623. X    Size2 = Sizex * Sizey;    /* surface area of the board */
  624. X
  625. X    switch (Facemethod)
  626. X    {
  627. X    case NUMBERFACES: 
  628. X        if (Size2 <= 10)    /* tiles can be labeled with 1 char */
  629. X        {
  630. X        Facechars = "123456789";
  631. X        Facemethod = NUMBERALPHAFACES;
  632. X        }
  633. X        break;
  634. X    case ALPHAFACES: 
  635. X        Facechars = "abcdefghijklmnopqrstuvwxyz";
  636. X        break;
  637. X    case NUMBERALPHAFACES: 
  638. X        Facechars = "0123456789abcdefghijklmnopqrstuvwxyz";
  639. X        break;
  640. X    case ALPHANUMBERFACES: 
  641. X        Facechars = "abcdefghijklmnopqrstuvwxyz0123456789";
  642. X        break;
  643. X    }
  644. X
  645. X    if ((Size2 - 1) > ((Facemethod == NUMBERFACES) ? 99 :
  646. X        strlen (Facechars)))
  647. X    {
  648. X    printf ("sorry, too many tiles - not enough labels\n");
  649. X    exit (0);
  650. X    }
  651. X    if ((Sizex < 2) || (Sizey < 2))
  652. X    {
  653. X    printf ("sorry, board is too small for a non-trivial game\n");
  654. X    exit (0);
  655. X    }
  656. X
  657. X /* 
  658. X  * Initialize the curses screen handling functions
  659. X  */
  660. X#ifndef NOSIGNAL
  661. X    signal (SIGINT, SIG_IGN);    /* protect this critical section */
  662. X#endif
  663. X    initscr ();
  664. X    crmode ();
  665. X    noecho ();
  666. X#ifdef ARROWKEYS
  667. X    keypad (stdscr, TRUE);
  668. X#endif
  669. X#ifndef NOSIGNAL
  670. X    signal (SIGINT, die);    /* die() knows how to restore ttymode */
  671. X#endif
  672. X /* 
  673. X  * From now on use die() to exit the program, otherwise the
  674. X  * ttymode will not be restored.
  675. X  *
  676. X  * Now that curses has been initialized we can check whether the board
  677. X  * fits on the screen.
  678. X  */
  679. X    if (Sizex > (COLS - 1) / 5)    /* COLS works only after initscr() */
  680. X    {
  681. X    mvprintw (LINES - 2, 0,
  682. X        "sorry, your screen is not wide enough\n");
  683. X    die ();
  684. X    }
  685. X    if (Sizey > (LINES - 4) / 2)
  686. X    {
  687. X    mvprintw (LINES - 2, 0,
  688. X        "sorry, your screen is not tall enough\n");
  689. X    die ();
  690. X    }
  691. X
  692. X    game ();            /* play the game */
  693. X
  694. X    mvprintw (LINES - 2, 0,
  695. X        "You've reached the solution in %d moves.\n", Movecnt);
  696. X    die ();            /* restore ttymode and exit */
  697. X}
  698. X
  699. Xusage ()
  700. X{
  701. X    int     curtype = UNSPECIFIED;
  702. X    int     prevtype = UNSPECIFIED;
  703. X    char   *selecttilename = (char *) 0;
  704. X    struct argtablemember  *atmp;/* to walk Argopts table */
  705. X
  706. X    printf ("usage: %s [<width[x<height>]] %s",
  707. X        Myname, "[movemethod] [cursormethod] [facemethod]\n");
  708. X    for (atmp = Argopts; atmp -> a_name != (char *) 0; atmp++)
  709. X    {
  710. X    if (atmp -> a_magic == SELECTTILE)
  711. X        selecttilename = atmp -> a_name;
  712. X    if (atmp -> a_type != curtype)
  713. X    {
  714. X        curtype = atmp -> a_type;
  715. X        if (curtype == MOVEMETHOD)
  716. X        printf ("possible movemethods:\n");
  717. X        if (curtype == CURSORMETHOD)
  718. X        printf ("possible cursormethods:\n");
  719. X        if (curtype == FACEMETHOD)
  720. X        printf ("possible facemethods:\n");
  721. X    }
  722. X    switch (atmp -> a_magic)
  723. X    {
  724. X        case KEYS: 
  725. X        printf ("\t\t%s <up><down><left><right>", atmp -> a_name);
  726. X        break;
  727. X        case FACES: 
  728. X        printf ("\t\t%s <facecharacters>", atmp -> a_name);
  729. X        break;
  730. X        case FACEKEYS: 
  731. X        if (selecttilename == (char *) 0)
  732. X        {
  733. X            printf ("aargh: can't find string for SELECTTILE\n");
  734. X            exit (1);
  735. X        }
  736. X        printf ("\t\t%-20.20s (implies movemethod %s)",
  737. X            atmp -> a_name, selecttilename);
  738. X        break;
  739. X        default: 
  740. X        printf ("\t\t%-20.20s", atmp -> a_name);
  741. X        break;
  742. X    }
  743. X    if (curtype != prevtype)
  744. X        printf (" (this is default)\n");
  745. X    else
  746. X        printf ("\n");
  747. X    prevtype = curtype;
  748. X    }
  749. X    exit (0);
  750. X}
  751. X
  752. Xconflict (word1, word2)
  753. Xchar   *word1;
  754. Xchar   *word2;
  755. X{
  756. X    printf ("You may not specify both %s and %s\n", word1, word2);
  757. X    usage ();
  758. X}
  759. X
  760. Xnovalue (word)
  761. Xchar   *word;
  762. X{
  763. X    printf ("%s requires an argument\n", word);
  764. X    usage ();
  765. X}
  766. X
  767. Xgame ()                /* initialize board, execute moves */
  768. X{
  769. X    register int    i, j;    /* generally used indices and counters */
  770. X    int    *board;        /* pointer to malloc-ed board */
  771. X    int     empty;        /* position of empty field */
  772. X    int     swap;        /* used to swap two tiles */
  773. X    int     nswap = 0;        /* to determine reachability */
  774. X    int     steps;        /* number of tiles that move */
  775. X    int     unchanged = 0;    /* used to indicate that board has changed */
  776. X    int     cursorx;        /* to save cursorposition while */
  777. X    int     cursory;        /*   printing error messages */
  778. X    int     m;            /* move, first character / direction */
  779. X    int     m2;            /* second character of move */
  780. X /* 
  781. X  * Set up the board and shuffle the tiles
  782. X  */
  783. X    board = (int *) malloc ((unsigned) Size2 * sizeof (int));
  784. X    if (board == NULL)
  785. X    {
  786. X    printf ("aargh: malloc failed\n");
  787. X    die ();
  788. X    }
  789. X    srand ((int) time ((long) 0));/* initialize random number generator */
  790. X    for (i = 1; i < Size2; i++)    /* put tiles on the board in their */
  791. X    board[i - 1] = i;    /*   final positions */
  792. X    for (i = Size2 - 2; i > 0; i--)/* permutate tiles */
  793. X    {
  794. X    j = rand () % i;
  795. X    swap = board[i];
  796. X    board[i] = board[j];
  797. X    board[j] = swap;
  798. X    }
  799. X /* 
  800. X  * Check that final position can be reached from current permutation
  801. X  */
  802. X    for (i = 0; i < Size2 - 1; i++)
  803. X    for (j = 0; j < i; j++)
  804. X        if (board[j] > board[i])
  805. X        nswap++;
  806. X    if ((nswap % 2) != 0)    /* this position is unreachable */
  807. X    {                /*   swap two adjacent tiles */
  808. X    swap = board[1];
  809. X    board[1] = board[0];
  810. X    board[0] = swap;
  811. X    }
  812. X    empty = Size2 - 1;        /* empty field starts in lower right */
  813. X    board[empty] = BLANK;    /*   corner */
  814. X    Newpos = empty;
  815. X    Time_start = time ((long *) 0);/* start the clock */
  816. X
  817. X    while (1)            /* until final position is reached */
  818. X    {
  819. X    if (unchanged == 0)    /* the board must be (re-)printed */
  820. X    {
  821. X        printboard (board);    /* also puts cursor at Newpos */
  822. X        unchanged++;    /* the board on the screen is up to date */
  823. X    /* 
  824. X     * Check if final position is reached
  825. X     */
  826. X        for (i = 0; i < Size2 - 1; i++)
  827. X        if (board[i] != i + 1)
  828. X            break;    /* final position is not yet reached */
  829. X        if (i == Size2 - 1)    /* all tiles are in final positions */
  830. X        return;        /* game ends */
  831. X    }
  832. X    /* 
  833. X     * Let the user make a move
  834. X     */
  835. X    m = getch ();
  836. X    if (m == DIECHAR)
  837. X        die ();
  838. X    if (Movemethod == SELECTTILE)
  839. X    {
  840. X        if (Cursormethod == FACEKEYS)
  841. X        {
  842. X        if (Facemethod == NUMBERFACES)
  843. X        {
  844. X            if (!isdigit (m))
  845. X            {
  846. X            getyx (stdscr, cursory, cursorx);
  847. X            mvprintw (LINES - 1, 0,
  848. X                "use one of the keys");
  849. X            for (i = 0; (i <= Size2) && (i < 10); i++)
  850. X                printw (" %d", i);
  851. X            clrtoeol ();
  852. X            move (cursory, cursorx);
  853. X            refresh ();
  854. X            continue;
  855. X            }
  856. X            if ((m - '0') > (Size2 / 10))
  857. X            m -= '0';
  858. X            else    /* we need a second digit */
  859. X            {
  860. X            m = (m - '0') * 10;
  861. X            m2 = getch ();
  862. X            if (m == DIECHAR)
  863. X                die ();
  864. X            if ((!isdigit (m2)) || (m + m2 - '0' >= Size2))
  865. X            {
  866. X                getyx (stdscr, cursory, cursorx);
  867. X                mvprintw (LINES - 1, 0,
  868. X                    "use one of the keys");
  869. X                for (i = 0; (i < Size2 % 10) && (i + m < Size2);
  870. X                    i++)
  871. X                printw (" %d", i);
  872. X                clrtoeol ();
  873. X                move (cursory, cursorx);
  874. X                refresh ();
  875. X                continue;
  876. X            }
  877. X            m += m2 - '0';
  878. X            }
  879. X        /* 
  880. X         * find out where this tile is on the board
  881. X         */
  882. X            for (Newpos = 0; Newpos < Size2; Newpos++)
  883. X            if (board[Newpos] == m)
  884. X                break;/* found tile */
  885. X            if (Newpos == Size2)/* no tile with face m */
  886. X            {
  887. X            mvprintw (LINES - 2, 0,
  888. X                "aargh: can't find tile %d on board\n", m);
  889. X            die ();
  890. X            }
  891. X        }
  892. X        else
  893. X        {
  894. X        /* 
  895. X         * Facemethod != NUMBERFACES
  896. X         * This means that a single keystroke identifies the
  897. X         * tile that is to be moved.
  898. X         */
  899. X            for (Newpos = 0; Newpos < Size2; Newpos++)
  900. X            if (board[Newpos] > 0)
  901. X                if (Facechars[board[Newpos] - 1] == m)
  902. X                break;/* found tile */
  903. X            if (Newpos == Size2)
  904. X            {
  905. X            getyx (stdscr, cursory, cursorx);
  906. X            mvprintw (LINES - 1, 0,
  907. X                "use one of the keys ");
  908. X            for (i = 0; (i < Size2 - 1) && (i < 30); i++)
  909. X                printw ("%c", Facechars[i]);
  910. X            if (i < Size2 - 1)
  911. X                printw ("...");
  912. X            clrtoeol ();
  913. X            move (cursory, cursorx);
  914. X            refresh ();
  915. X            continue;
  916. X            }
  917. X        }
  918. X        }
  919. X        else        /* Cursormethod != FACEKEYS */
  920. X        {
  921. X        if (m == Up)
  922. X        {
  923. X            if (Newpos >= Sizex)
  924. X            Newpos -= Sizex;
  925. X            unchanged = 0;/* board must be reprinted */
  926. X            continue;
  927. X        }
  928. X        if (m == Down)
  929. X        {
  930. X            if (Newpos + Sizex < Size2)
  931. X            Newpos += Sizex;
  932. X            unchanged = 0;
  933. X            continue;
  934. X        }
  935. X        if (m == Left)
  936. X        {
  937. X            if ((Newpos % Sizex) != 0)
  938. X            Newpos--;
  939. X            unchanged = 0;
  940. X            continue;
  941. X        }
  942. X        if (m == Right)
  943. X        {
  944. X            if (((Newpos + 1) % Sizex) != 0)
  945. X            Newpos++;
  946. X            unchanged = 0;
  947. X            continue;
  948. X        }
  949. X        /* 
  950. X         * If a key not in the set { Up, Down, Left, Right } was
  951. X         * typed we fall through and try to move the empty field to
  952. X         * Newpos.
  953. X         */
  954. X        }
  955. X    /* 
  956. X     * The user has indicated a new location for the empty field.
  957. X     * The new position of the empty field in the array board is in
  958. X     * Newpos.
  959. X     * We must now check that the new position is on the same row
  960. X     * or the same column as the current position and we must
  961. X     * determine how many tiles must be moved.
  962. X     */
  963. X        if (Newpos == empty)
  964. X        continue;    /* nothing changed */
  965. X        steps = 0;
  966. X        if (Newpos > empty)
  967. X        {
  968. X        if ((empty % Sizex + Newpos - empty) < Sizex)
  969. X        {
  970. X            m = Right;
  971. X            steps = Newpos - empty;
  972. X        }
  973. X        else
  974. X            if (((Newpos - empty) % Sizex) == 0)
  975. X            {
  976. X            m = Down;
  977. X            steps = (Newpos - empty) / Sizex;
  978. X            }
  979. X        }
  980. X        else
  981. X        {
  982. X        if (empty % Sizex + Newpos - empty >= 0)
  983. X        {
  984. X            m = Left;
  985. X            steps = empty - Newpos;
  986. X        }
  987. X        else
  988. X            if (((empty - Newpos) % Sizex) == 0)
  989. X            {
  990. X            m = Up;
  991. X            steps = (empty - Newpos) / Sizex;
  992. X            }
  993. X        }
  994. X        if (steps == 0)
  995. X        {
  996. X        getyx (stdscr, cursory, cursorx);
  997. X        mvprintw (LINES - 1, 0,
  998. X            "tile must be in same row as empty field\n");
  999. X        move (cursory, cursorx);
  1000. X        refresh ();
  1001. X
  1002. X        continue;
  1003. X        }
  1004. X    }
  1005. X    else            /* Movemethod is MOVEBLANK or MOVETILE */
  1006. X        steps = 1;        /* one step per move */
  1007. X    /* 
  1008. X     * m should now be one of the four directions, but it may be an
  1009. X     * illegal key. This can not happen if Movemethod == SELECTTILE.
  1010. X     *
  1011. X     * steps indicates how many tiles are to be moved
  1012. X     */
  1013. X    if ((m != Up) && (m != Down) && (m != Left) && (m != Right))
  1014. X    {
  1015. X        getyx (stdscr, cursory, cursorx);
  1016. X#ifdef ARROWKEYS
  1017. X        if (Cursormethod == ARROWK)
  1018. X        mvprintw (LINES - 1, 0,
  1019. X            "Use the arrow keys for up, down, left and  right");
  1020. X        else
  1021. X#endif
  1022. X        if (Movemethod == MOVETILE)
  1023. X        {
  1024. X            mvprintw (LINES - 1, 0,
  1025. X                "use %c for up, %c for down, ", Down, Up);
  1026. X            printw ("%c for left and %c for right", Right, Left);
  1027. X        }
  1028. X        else
  1029. X        {
  1030. X            mvprintw (LINES - 1, 0,
  1031. X                "use %c for up, %c for down, ", Up, Down);
  1032. X            printw ("%c for left and %c for right", Left, Right);
  1033. X        }
  1034. X        clrtoeol ();
  1035. X        move (cursory, cursorx);
  1036. X        refresh ();
  1037. X        continue;
  1038. X    }
  1039. X    /* 
  1040. X     * m contains the direction to move
  1041. X     * steps contains the number of tiles to move
  1042. X     * Apply the move to the board.
  1043. X     */
  1044. X    if (m == Up)
  1045. X        if (empty >= Sizex)
  1046. X        while (steps-- > 0)
  1047. X        {
  1048. X            board[empty] = board[empty - Sizex];
  1049. X            board[empty - Sizex] = BLANK;
  1050. X            empty -= Sizex;
  1051. X            Movecnt++;
  1052. X        }
  1053. X    if (m == Down)
  1054. X        if (empty + Sizex < Size2)
  1055. X        while (steps-- > 0)
  1056. X        {
  1057. X            board[empty] = board[empty + Sizex];
  1058. X            board[empty + Sizex] = BLANK;
  1059. X            empty += Sizex;
  1060. X            Movecnt++;
  1061. X        }
  1062. X    if (m == Left)
  1063. X        if ((empty % Sizex) != 0)
  1064. X        while (steps-- > 0)
  1065. X        {
  1066. X            board[empty] = board[empty - 1];
  1067. X            board[empty - 1] = BLANK;
  1068. X            empty--;
  1069. X            Movecnt++;
  1070. X        }
  1071. X    if (m == Right)
  1072. X        if (((empty + 1) % Sizex) != 0)
  1073. X        while (steps-- > 0)
  1074. X        {
  1075. X            board[empty] = board[empty + 1];
  1076. X            board[empty + 1] = BLANK;
  1077. X            empty++;
  1078. X            Movecnt++;
  1079. X        }
  1080. X    if (steps == 1)        /* you ran into a wall */
  1081. X    {
  1082. X        getyx (stdscr, cursory, cursorx);
  1083. X        mvprintw (LINES - 1, 0,
  1084. X            "Your can't cross that wall\n");
  1085. X        clrtoeol ();
  1086. X        move (cursory, cursorx);
  1087. X        refresh ();
  1088. X        continue;
  1089. X    }
  1090. X    if (steps != -1)    /* something is very wrong */
  1091. X    {
  1092. X        mvprintw (LINES - 2, 0,
  1093. X            "aargh: couldn't move enough tiles (steps = %d)\n",
  1094. X            steps);
  1095. X        die ();
  1096. X    }
  1097. X    Newpos = empty;
  1098. X    unchanged = 0;        /* the board must be reprinted */
  1099. X    }
  1100. X}
  1101. X
  1102. Xprintboard (board)
  1103. Xint    *board;
  1104. X{
  1105. X    register int    i, j;
  1106. X    int     tilewidth;
  1107. X    unsigned long   time_used;
  1108. X    unsigned    minutes;
  1109. X    unsigned    seconds;
  1110. X    unsigned long   time_now;
  1111. X
  1112. X    tilewidth = ((Facemethod == NUMBERFACES) && (Size2 > 10)) ? 5 : 4;
  1113. X    mvprintw ((LINES - 4 - 2 * Sizey) / 2,
  1114. X        (COLS - 1 - tilewidth * Sizex) / 2,
  1115. X        "+");        /* print top edge of board */
  1116. X    for (j = 0; j < Sizex; j++)
  1117. X    if (tilewidth == 5)
  1118. X        printw ("----+");
  1119. X    else
  1120. X        printw ("---+");
  1121. X    for (i = 0; i < Sizey; i++)
  1122. X    {
  1123. X    mvprintw ((LINES - 4 - 2 * Sizey) / 2 + i * 2 + 1,
  1124. X        (COLS - 1 - tilewidth * Sizex) / 2, "| ");
  1125. X    for (j = 0; j < Sizex; j++)
  1126. X        if (tilewidth == 5)
  1127. X        if (board[Sizex * i + j] != BLANK)
  1128. X            if ((Size2 > 9) && (Cursormethod == FACEKEYS) &&
  1129. X                (board[Sizex * i + j] <= Size2 / 10))
  1130. X            printw ("%02d | ", board[Sizex * i + j]);
  1131. X            else
  1132. X            printw ("%2d | ", board[Sizex * i + j]);
  1133. X        else
  1134. X            printw ("   | ");
  1135. X        else
  1136. X        if (board[Sizex * i + j] != BLANK)
  1137. X            printw ("%c | ", Facechars[board[Sizex * i + j] - 1]);
  1138. X        else
  1139. X            printw ("  | ");
  1140. X    mvprintw ((LINES - 4 - 2 * Sizey) / 2 + i * 2 + 2,
  1141. X        (COLS - 1 - tilewidth * Sizex) / 2, "+");
  1142. X    for (j = 0; j < Sizex; j++)
  1143. X        if (tilewidth == 5)
  1144. X        printw ("----+");
  1145. X        else
  1146. X        printw ("---+");
  1147. X    }
  1148. X    mvprintw (LINES - 1, 0, "\n");/* erase error messages */
  1149. X /* 
  1150. X  * Update the clock
  1151. X  */
  1152. X    time_now = time ((long *) 0);
  1153. X    time_used = time_now - Time_start;
  1154. X    minutes = time_used / 60;
  1155. X    seconds = time_used % 60;
  1156. X    mvprintw (LINES - 3, 0, "Time used: %02d min %02d sec   Move: %d",
  1157. X        minutes, seconds, Movecnt);
  1158. X /* 
  1159. X  * Put cursor on the position indicated by Newpos
  1160. X  */
  1161. X    move ((LINES - 4 - 2 * Sizey) / 2 + (Newpos / Sizex) * 2 + 1,
  1162. X        (COLS - 1 - tilewidth * Sizex) / 2 +
  1163. X        (Newpos % Sizex) * tilewidth + tilewidth / 2);
  1164. X    refresh ();            /* put all this on the screen */
  1165. X}
  1166. END_OF_FILE
  1167. if test 23213 -ne `wc -c <'puzzle15.c'`; then
  1168.     echo shar: \"'puzzle15.c'\" unpacked with wrong size!
  1169. fi
  1170. # end of 'puzzle15.c'
  1171. fi
  1172. echo shar: End of shell archive.
  1173. exit 0
  1174.